🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / feature / firmware / src / commonMain / kotlin / org / meshtastic / feature / firmware / ota / BleOtaTransport.kt
Displaying Raw • Download
feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransport.kt 9c8532f80d33a3b24fab5bb7d74ea3ad9b14305f (9c8532f8) Text, 12.63 KB
T8b949e/*
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.firmware.ota
Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CompletableDeferred
Tff7b72import T7ee787kotlinx.coroutines.CoroutineDispatcher
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.Dispatchers
Tff7b72import T7ee787kotlinx.coroutines.SupervisorJob
Tff7b72import T7ee787kotlinx.coroutines.cancel
Tff7b72import T7ee787kotlinx.coroutines.channels.Channel
Tff7b72import T7ee787kotlinx.coroutines.delay
Tff7b72import T7ee787kotlinx.coroutines.flow.catch
Tff7b72import T7ee787kotlinx.coroutines.flow.launchIn
Tff7b72import T7ee787kotlinx.coroutines.flow.onEach
Tff7b72import T7ee787kotlinx.coroutines.withTimeout
Tff7b72import T7ee787org.meshtastic.core.ble.BleCharacteristic
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnectionFactory
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnectionState
Tff7b72import T7ee787org.meshtastic.core.ble.BleDevice
Tff7b72import T7ee787org.meshtastic.core.ble.BleScanner
Tff7b72import T7ee787org.meshtastic.core.ble.BleWriteType
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.OTA_NOTIFY_CHARACTERISTIC
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.OTA_SERVICE_UUID
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.OTA_WRITE_CHARACTERISTIC
T8b949e/** BLE transport implementation for ESP32 Unified OTA protocol using Kable. */
Tff7b72class T56d364BleOtaTransportTb4b4b4(
Tff7b72private Tff7b72val Te6edf3scannerTb4b4b4: Te6edf3BleScannerTb4b4b4,
Te6edf3connectionFactoryTb4b4b4: Te6edf3BleConnectionFactoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3dispatcherTb4b4b4: Te6edf3CoroutineDispatcher Tff7b72= Te6edf3DispatchersTb4b4b4.Te6edf3DefaultTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3UnifiedOtaProtocol Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3transportScope Tff7b72= Te6edf3CoroutineScopeTb4b4b4(Te6edf3SupervisorJobTb4b4b4(Tb4b4b4) Tff7b72+ Te6edf3dispatcherTb4b4b4)
Tff7b72private Tff7b72val Te6edf3bleConnection Tff7b72= Te6edf3connectionFactoryTb4b4b4.Te6edf3createTb4b4b4(Te6edf3transportScopeTb4b4b4, Ta5d6ff"Ta5d6ffBLE OTATa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3otaChar Tff7b72= Te6edf3BleCharacteristicTb4b4b4(Te6edf3OTA_WRITE_CHARACTERISTICTb4b4b4)
Tff7b72private Tff7b72val Te6edf3txChar Tff7b72= Te6edf3BleCharacteristicTb4b4b4(Te6edf3OTA_NOTIFY_CHARACTERISTICTb4b4b4)
Tff7b72private Tff7b72val Te6edf3responseChannel Tff7b72= Te6edf3ChannelTff7b72<Tffa657StringTff7b72>Tb4b4b4(Te6edf3ChannelTb4b4b4.Te6edf3UNLIMITEDTb4b4b4)
Tff7b72private Tff7b72var Te6edf3isConnected Tff7b72= Tff7b72false
T8b949e/** Scan for the device by MAC address (or MAC+1 for OTA mode) with retries. */
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffscanForOtaDeviceTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3BleDevice? Tb4b4b4{
Tff7b72val Te6edf3otaAddress Tff7b72= Te6edf3calculateMacPlusOneTb4b4b4(Te6edf3addressTb4b4b4)
Tff7b72val Te6edf3targetAddresses Tff7b72= Te6edf3setOfTb4b4b4(Te6edf3addressTb4b4b4, Te6edf3otaAddressTb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Will match addresses: Tffd700$Te6edf3targetAddressesTa5d6ff" Tb4b4b4}
Tff7b72return Te6edf3scanForBleDeviceTb4b4b4(
Te6edf3scanner Tff7b72= Te6edf3scannerTb4b4b4,
Te6edf3tag Tff7b72= Ta5d6ff"Ta5d6ffBLE OTATa5d6ff"Tb4b4b4,
Te6edf3serviceUuid Tff7b72= Te6edf3OTA_SERVICE_UUIDTb4b4b4,
Te6edf3retryCount Tff7b72= Te6edf3SCAN_RETRY_COUNTTb4b4b4,
Te6edf3retryDelayMs Tff7b72= Te6edf3SCAN_RETRY_DELAY_MSTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tffa657itTb4b4b4.Te6edf3address Tff7b72in Te6edf3targetAddresses
Tb4b4b4}
Tb4b4b4}
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffMagicNumberTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffconnectTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ResultTff7b72<Tffa657UnitTff7b72> Tff7b72= Te6edf3runCatching Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Waiting Tffd700${Te6edf3REBOOT_DELAY_MSTffd700}Ta5d6ffms for device to reboot into OTA mode...Ta5d6ff" Tb4b4b4}
Te6edf3delayTb4b4b4(Te6edf3REBOOT_DELAY_MSTb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Connecting to Tffd700$Te6edf3addressTa5d6ff using Kable...Ta5d6ff" Tb4b4b4}
Tff7b72val Te6edf3device Tff7b72=
Te6edf3scanForOtaDeviceTb4b4b4(Tb4b4b4)
Tff7b72?: Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3ConnectionFailedTb4b4b4(
Ta5d6ff"Ta5d6ffDevice not found at address Tffd700$Te6edf3addressTa5d6ff. Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffEnsure the device has rebooted into OTA mode and is advertising.Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3bleConnectionTb4b4b4.Te6edf3connectionState
Tb4b4b4.Te6edf3onEach Tb4b4b4{ Te6edf3state Tff7b72-Tff7b72>
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Connection state changed to Tffd700$Te6edf3stateTa5d6ff" Tb4b4b4}
Te6edf3isConnected Tff7b72= Te6edf3state Tff7b72is Te6edf3BleConnectionStateTb4b4b4.Te6edf3Connected
Tb4b4b4}
Tb4b4b4.Te6edf3launchInTb4b4b4(Te6edf3transportScopeTb4b4b4)
Tff7b72try Tb4b4b4{
Tff7b72val Te6edf3finalState Tff7b72= Te6edf3bleConnectionTb4b4b4.Te6edf3connectAndAwaitTb4b4b4(Te6edf3deviceTb4b4b4, Te6edf3CONNECTION_TIMEOUT_MSTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3finalState Tff7b72is Te6edf3BleConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Failed to connect to Tffd700${Te6edf3deviceTb4b4b4.Te6edf3addressTffd700}Ta5d6ff (state=Tffd700$Te6edf3finalStateTa5d6ff)Ta5d6ff" Tb4b4b4}
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3ConnectionFailedTb4b4b4(Ta5d6ff"Ta5d6ffFailed to connect to device at address Tffd700${Te6edf3deviceTb4b4b4.Te6edf3addressTffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffSwallowedExceptionTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3kotlinxTb4b4b4.Te6edf3coroutinesTb4b4b4.Te6edf3TimeoutCancellationExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Timed out waiting to connect to Tffd700${Te6edf3deviceTb4b4b4.Te6edf3addressTffd700}Ta5d6ff. Error: Tffd700${Te6edf3eTb4b4b4.Te6edf3messageTffd700}Ta5d6ff" Tb4b4b4}
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TimeoutTb4b4b4(Ta5d6ff"Ta5d6ffTimed out connecting to device at address Tffd700${Te6edf3deviceTb4b4b4.Te6edf3addressTffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4}
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Connected to Tffd700${Te6edf3deviceTb4b4b4.Te6edf3addressTffd700}Ta5d6ff, discovering services...Ta5d6ff" Tb4b4b4}
Te6edf3bleConnectionTb4b4b4.Te6edf3profileTb4b4b4(Te6edf3OTA_SERVICE_UUIDTb4b4b4) Tb4b4b4{ Te6edf3service Tff7b72-Tff7b72>
T8b949e// Log negotiated MTU for diagnostics
Tff7b72val Te6edf3maxLen Tff7b72= Te6edf3bleConnectionTb4b4b4.Te6edf3maximumWriteValueLengthTb4b4b4(Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITHOUT_RESPONSETb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Service ready. Max write value length: Tffd700$Te6edf3maxLenTa5d6ff bytesTa5d6ff" Tb4b4b4}
T8b949e// Enable notifications and collect responses
Tff7b72val Te6edf3subscribed Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Te6edf3service
Tb4b4b4.Te6edf3observeTb4b4b4(Te6edf3txCharTb4b4b4)
Tb4b4b4.Te6edf3onEach Tb4b4b4{ Te6edf3notifyBytes Tff7b72-Tff7b72>
Tff7b72try Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3subscribedTb4b4b4.Te6edf3isCompletedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: TX characteristic subscribedTa5d6ff" Tb4b4b4}
Te6edf3subscribedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Tb4b4b4}
Tff7b72val Te6edf3response Tff7b72= Te6edf3notifyBytesTb4b4b4.Te6edf3decodeToStringTb4b4b4(Tb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Received response: Tffd700$Te6edf3responseTa5d6ff" Tb4b4b4}
Te6edf3responseChannelTb4b4b4.Te6edf3trySendTb4b4b4(Te6edf3responseTb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Failed to decode response bytesTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3catch Tb4b4b4{ Te6edf3e Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3subscribedTb4b4b4.Te6edf3isCompletedTb4b4b4) Te6edf3subscribedTb4b4b4.Te6edf3completeExceptionallyTb4b4b4(Te6edf3eTb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Error in TX characteristic subscriptionTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3launchInTb4b4b4(Tff7b72thisTb4b4b4)
T8b949e// Allow time for the BLE subscription to be established before proceeding.
Te6edf3delayTb4b4b4(Te6edf3SUBSCRIPTION_SETTLE_MSTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3subscribedTb4b4b4.Te6edf3isCompletedTb4b4b4) Te6edf3subscribedTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3subscribedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Service discovered and readyTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffstartOtaTb4b4b4(
Te6edf3sizeBytesTb4b4b4: Tffa657LongTb4b4b4,
Te6edf3sha256HashTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3onHandshakeStatusTb4b4b4: Te6edf3suspend Tb4b4b4(Te6edf3OtaHandshakeStatusTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3ResultTff7b72<Tffa657UnitTff7b72> Tff7b72= Te6edf3runCatching Tb4b4b4{
Tff7b72val Te6edf3command Tff7b72= Te6edf3OtaCommandTb4b4b4.Te6edf3StartOtaTb4b4b4(Te6edf3sizeBytesTb4b4b4, Te6edf3sha256HashTb4b4b4)
Tff7b72val Te6edf3packetsSent Tff7b72= Te6edf3sendCommandTb4b4b4(Te6edf3commandTb4b4b4)
Tff7b72var Te6edf3handshakeComplete Tff7b72= Tff7b72false
Tff7b72var Te6edf3responsesReceived Tff7b72= T79c0ff0
Tff7b72while Tb4b4b4(Tff7b72!Te6edf3handshakeCompleteTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3response Tff7b72= Te6edf3waitForResponseTb4b4b4(Te6edf3ERASING_TIMEOUT_MSTb4b4b4)
Te6edf3responsesReceivedTff7b72+Tff7b72+
Tff7b72when Tb4b4b4(Tff7b72val Te6edf3parsed Tff7b72= Te6edf3OtaResponseTb4b4b4.Te6edf3parseTb4b4b4(Te6edf3responseTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Ok Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3responsesReceived Tff7b72>Tff7b72= Te6edf3packetsSentTb4b4b4) Tb4b4b4{
Te6edf3handshakeComplete Tff7b72= Tff7b72true
Tb4b4b4}
Tb4b4b4}
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Erasing Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Device erasing flash...Ta5d6ff" Tb4b4b4}
Te6edf3onHandshakeStatusTb4b4b4(Te6edf3OtaHandshakeStatusTb4b4b4.Te6edf3ErasingTb4b4b4)
Tb4b4b4}
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Error Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3parsedTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffHash RejectedTa5d6ff"Tb4b4b4, Te6edf3ignoreCase Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3HashRejectedTb4b4b4(Te6edf3sha256HashTb4b4b4)
Tb4b4b4}
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3CommandFailedTb4b4b4(Te6edf3commandTb4b4b4, Te6edf3parsedTb4b4b4)
Tb4b4b4}
Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffBLE OTA: Unexpected handshake response: Tffd700$Te6edf3responseTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffMagicNumberTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffstreamFirmwareTb4b4b4(
Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4,
Te6edf3chunkSizeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3onProgressTb4b4b4: Te6edf3suspend Tb4b4b4(Tffa657FloatTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3ResultTff7b72<Tffa657UnitTff7b72> Tff7b72= Te6edf3runCatching Tb4b4b4{
Tff7b72val Te6edf3totalBytes Tff7b72= Te6edf3dataTb4b4b4.Te6edf3size
Tff7b72var Te6edf3sentBytes Tff7b72= T79c0ff0
Tff7b72while Tb4b4b4(Te6edf3sentBytes Tff7b72< Te6edf3totalBytesTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3isConnectedTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TransferFailedTb4b4b4(Ta5d6ff"Ta5d6ffConnection lost during transferTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72val Te6edf3remainingBytes Tff7b72= Te6edf3totalBytes Tff7b72- Te6edf3sentBytes
Tff7b72val Te6edf3currentChunkSize Tff7b72= Te6edf3minOfTb4b4b4(Te6edf3chunkSizeTb4b4b4, Te6edf3remainingBytesTb4b4b4)
Tff7b72val Te6edf3chunk Tff7b72= Te6edf3dataTb4b4b4.Te6edf3copyOfRangeTb4b4b4(Te6edf3sentBytesTb4b4b4, Te6edf3sentBytes Tff7b72+ Te6edf3currentChunkSizeTb4b4b4)
Tff7b72val Te6edf3packetsSentForChunk Tff7b72= Te6edf3writeDataTb4b4b4(Te6edf3chunkTb4b4b4, Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITHOUT_RESPONSETb4b4b4)
Tff7b72val Te6edf3nextSentBytes Tff7b72= Te6edf3sentBytes Tff7b72+ Te6edf3currentChunkSize
Te6edf3repeatTb4b4b4(Te6edf3packetsSentForChunkTb4b4b4) Tb4b4b4{ Te6edf3i Tff7b72-Tff7b72>
Tff7b72val Te6edf3response Tff7b72= Te6edf3waitForResponseTb4b4b4(Te6edf3ACK_TIMEOUT_MSTb4b4b4)
Tff7b72val Te6edf3isLastPacketOfChunk Tff7b72= Te6edf3i Tff7b72=Tff7b72= Te6edf3packetsSentForChunk Tff7b72- T79c0ff1
Tff7b72when Tb4b4b4(Tff7b72val Te6edf3parsed Tff7b72= Te6edf3OtaResponseTb4b4b4.Te6edf3parseTb4b4b4(Te6edf3responseTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Ack Tff7b72-Tff7b72> Tb4b4b4{Tb4b4b4}
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Ok Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3nextSentBytes Tff7b72>Tff7b72= Te6edf3totalBytes Tff7b72&Tff7b72& Te6edf3isLastPacketOfChunkTb4b4b4) Tb4b4b4{
Te6edf3sentBytes Tff7b72= Te6edf3nextSentBytes
Te6edf3onProgressTb4b4b4(T79c0ff1.0fTb4b4b4)
Tff7b72returnTf0883e@runCatching Tffa657Unit
Tb4b4b4}
Tb4b4b4}
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Error Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3parsedTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffHash MismatchTa5d6ff"Tb4b4b4, Te6edf3ignoreCase Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3VerificationFailedTb4b4b4(Ta5d6ff"Ta5d6ffFirmware hash mismatch after transferTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TransferFailedTb4b4b4(Ta5d6ff"Ta5d6ffTransfer failed: Tffd700${Te6edf3parsedTb4b4b4.Te6edf3messageTffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72else Tff7b72-Tff7b72> Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TransferFailedTb4b4b4(Ta5d6ff"Ta5d6ffUnexpected response: Tffd700$Te6edf3responseTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Te6edf3sentBytes Tff7b72= Te6edf3nextSentBytes
Te6edf3onProgressTb4b4b4(Te6edf3sentBytesTb4b4b4.Te6edf3toFloatTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3totalBytesTb4b4b4)
Tb4b4b4}
Tff7b72val Te6edf3finalResponse Tff7b72= Te6edf3waitForResponseTb4b4b4(Te6edf3VERIFICATION_TIMEOUT_MSTb4b4b4)
Tff7b72when Tb4b4b4(Tff7b72val Te6edf3parsed Tff7b72= Te6edf3OtaResponseTb4b4b4.Te6edf3parseTb4b4b4(Te6edf3finalResponseTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Ok Tff7b72-Tff7b72> Tffa657Unit
Tff7b72is Te6edf3OtaResponseTb4b4b4.Te6edf3Error Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3parsedTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffHash MismatchTa5d6ff"Tb4b4b4, Te6edf3ignoreCase Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3VerificationFailedTb4b4b4(Ta5d6ff"Ta5d6ffFirmware hash mismatch after transferTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TransferFailedTb4b4b4(Ta5d6ff"Ta5d6ffVerification failed: Tffd700${Te6edf3parsedTb4b4b4.Te6edf3messageTffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72else Tff7b72-Tff7b72> Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TransferFailedTb4b4b4(Ta5d6ff"Ta5d6ffExpected OK after transfer, got: Tffd700$Te6edf3parsedTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffcloseTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3bleConnectionTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4)
Te6edf3isConnected Tff7b72= Tff7b72false
Te6edf3transportScopeTb4b4b4.Te6edf3cancelTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffsendCommandTb4b4b4(Te6edf3commandTb4b4b4: Te6edf3OtaCommandTb4b4b4)Tb4b4b4: Tffa657Int Tb4b4b4{
Tff7b72val Te6edf3data Tff7b72= Te6edf3commandTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3encodeToByteArrayTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3writeDataTb4b4b4(Te6edf3dataTb4b4b4, Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITH_RESPONSETb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffwriteDataTb4b4b4(Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4, Te6edf3writeTypeTb4b4b4: Te6edf3BleWriteTypeTb4b4b4)Tb4b4b4: Tffa657Int Tb4b4b4{
Tff7b72val Te6edf3maxLen Tff7b72= Te6edf3bleConnectionTb4b4b4.Te6edf3maximumWriteValueLengthTb4b4b4(Te6edf3writeTypeTb4b4b4) Tff7b72?: Te6edf3dataTb4b4b4.Te6edf3size
Tff7b72var Te6edf3offset Tff7b72= T79c0ff0
Tff7b72var Te6edf3packetsSent Tff7b72= T79c0ff0
Tff7b72try Tb4b4b4{
Tff7b72while Tb4b4b4(Te6edf3offset Tff7b72< Te6edf3dataTb4b4b4.Te6edf3sizeTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3chunkSize Tff7b72= Te6edf3minOfTb4b4b4(Te6edf3dataTb4b4b4.Te6edf3size Tff7b72- Te6edf3offsetTb4b4b4, Te6edf3maxLenTb4b4b4)
Tff7b72val Te6edf3packet Tff7b72= Te6edf3dataTb4b4b4.Te6edf3copyOfRangeTb4b4b4(Te6edf3offsetTb4b4b4, Te6edf3offset Tff7b72+ Te6edf3chunkSizeTb4b4b4)
Te6edf3bleConnectionTb4b4b4.Te6edf3profileTb4b4b4(Te6edf3OTA_SERVICE_UUIDTb4b4b4) Tb4b4b4{ Te6edf3service Tff7b72-Tff7b72> Te6edf3serviceTb4b4b4.Te6edf3writeTb4b4b4(Te6edf3otaCharTb4b4b4, Te6edf3packetTb4b4b4, Te6edf3writeTypeTb4b4b4) Tb4b4b4}
Te6edf3offset Tff7b72+Tff7b72= Te6edf3chunkSize
Te6edf3packetsSentTff7b72+Tff7b72+
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TransferFailedTb4b4b4(Ta5d6ff"Ta5d6ffFailed to write data at offset Tffd700$Te6edf3offsetTa5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3packetsSent
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffwaitForResponseTb4b4b4(Te6edf3timeoutMsTb4b4b4: Tffa657LongTb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Tff7b72try Tb4b4b4{
Te6edf3withTimeoutTb4b4b4(Te6edf3timeoutMsTb4b4b4) Tb4b4b4{ Te6edf3responseChannelTb4b4b4.Te6edf3receiveTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffSwallowedExceptionTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3kotlinxTb4b4b4.Te6edf3coroutinesTb4b4b4.Te6edf3TimeoutCancellationExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3TimeoutTb4b4b4(Ta5d6ff"Ta5d6ffTimeout waiting for response after Tffd700${Te6edf3timeoutMsTffd700}Ta5d6ffmsTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72private Tff7b72const Tff7b72val Te6edf3CONNECTION_TIMEOUT_MS Tff7b72= T79c0ff1T79c0ff5Te6edf3_000L
Tff7b72private Tff7b72const Tff7b72val Te6edf3SUBSCRIPTION_SETTLE_MS Tff7b72= T79c0ff5T79c0ff0T79c0ff0L
Tff7b72private Tff7b72const Tff7b72val Te6edf3ERASING_TIMEOUT_MS Tff7b72= T79c0ff6T79c0ff0Te6edf3_000L
Tff7b72private Tff7b72const Tff7b72val Te6edf3ACK_TIMEOUT_MS Tff7b72= T79c0ff1T79c0ff0Te6edf3_000L
Tff7b72private Tff7b72const Tff7b72val Te6edf3VERIFICATION_TIMEOUT_MS Tff7b72= T79c0ff1T79c0ff0Te6edf3_000L
Tff7b72private Tff7b72const Tff7b72val Te6edf3REBOOT_DELAY_MS Tff7b72= T79c0ff5Te6edf3_000L
Tff7b72private Tff7b72const Tff7b72val Te6edf3SCAN_RETRY_COUNT Tff7b72= T79c0ff3
Tff7b72private Tff7b72const Tff7b72val Te6edf3SCAN_RETRY_DELAY_MS Tff7b72= T79c0ff2Te6edf3_000L
Tff7b72const Tff7b72val Te6edf3RECOMMENDED_CHUNK_SIZE Tff7b72= T79c0ff5T79c0ff1T79c0ff2
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.4 - Generated in 0.07s